/* Copyright 2009 Sony Mathew, Minneapolis MN. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
package com.xsm.lite.util;

import java.util.Map;
import java.util.Set;

/**
 * A Many to Many map from A to B and vice-versa B to A.
 * A Light version - use in place of commons BidiMap or similar.
 * 
 * Notes:
 * Not Thread Safe.
 * Does not allow duplicates.
 * 
 * @author Sony Mathew
 */
public class Many2ManyMap<A, B> {

    private One2ManyMap<A, B> mapA2B = new One2ManyMap<A, B>();
    private One2ManyMap<B, A> mapB2A = new One2ManyMap<B, A>();
    
    /**
     * Creats a mapping between a and b and vice-versa.
     * 
     * author Sony Mathew
     */
    public void add(A a, B b) {
        mapA2B.add(a, b);
        mapB2A.add(b, a);
    }
    
    /**
     * Indicate wether a mapping from a->b or b->a exists.
     * 
     * author Sony Mathew
     */
    public boolean exists(A a, B b) {
        return mapA2B.exists(a, b) || mapB2A.exists(b, a);
    }
    
    /**
     * Removes this A to B and B to A relationship
     * 
     * author Sony Mathew
     */
    public void remove(A a, B b) {
        mapA2B.remove(a, b);
        mapB2A.remove(b, a);        
    }
    
    /**
     * Removes all A occurrences of given A from this Map (both directions).
     * 
     * author Sony Mathew
     */
    public void removeA(A a) {
        Set<B> bs = mapA2B.get(a);
        if (bs != null) {
            for (B b : bs) {
                mapB2A.remove(b, a);
            }
        }
        mapA2B.remove(a);
    }
    
    /**
     * Removes all A occurrences of given B from this Map (both directions).
     * 
     * author Sony Mathew
     */
    public void removeB(B b) {
        Set<A> as = mapB2A.get(b);
        if (as != null) {
            for (A a : as) {
                mapA2B.remove(a, b);
            }
        }
        mapB2A.remove(b);
    }
    
    /**
     * @return Set<B> : all B elements mapped to given A.
     *  Can be null if no mappings exist for this A.
     *  
     * author Sony Mathew
     */
    public Set<B> getB(A a) {
        return mapA2B.get(a);
    }
    
    /**
     * @return Set<A> : all A elements mapped to given B.
     *  Can be null if no mappings exist for this B.
     *  
     * author Sony Mathew
     */
    public Set<A> getA(B b) {
        return mapB2A.get(b);
    }
    
    /**
     * Obtain all the A elements.
     * 
     * author Sony Mathew
     */
    public Set<A> allA() {
        return mapA2B.allKeys();
    }

    /**
     * Obtain all the B elements.
     * 
     * author Sony Mathew
     */
    public Set<B> allB() {
        return mapB2A.allKeys();
    }
    
    /**
     * Obtain (readonly) the the Map of A to many Bs directly.
     * 
     * author Sony Mathew
     */
    public Map<A, Set<B>> getMapA2B() {
        return mapA2B.getMap();
    }

    /**
     * Obtain (readonly) the the Map of B to many As directly.
     * 
     * author Sony Mathew
     */
    public Map<B, Set<A>> getMapB2A() {
        return mapB2A.getMap();
    }
    
    /**
     * Removes everything.
     * 
     * author Sony Mathew
     */
    public void clear() {
        mapA2B.clear();
        mapB2A.clear();
    }
    
}
